#ifndef DIRECTX_MESH_H_
#define DIRECTX_MESH_H_

#include <d3dx9.h>
#include <string>
#include <vector>
#include "File.h"

/* The main vertex supported by this rendering engine. */
struct CustomVertex
{
    CustomVertex(): 
                    pos(0,0,0, 1),
                    normal(0,0,0, 1),
                    texCoord(0,0)
    {}	
    CustomVertex(float posX, float posY, float posZ,
                 float norX, float norY, float norZ,
                 float UVx, float UVy) : 
                                     pos(posX,posY, posZ, 1),
                                     normal(norX, norY, norZ, 1),
                                     texCoord(UVx, UVy)
    {}

    D3DXVECTOR4 pos;
    D3DXVECTOR4 normal;
    D3DXVECTOR2 texCoord;

    static IDirect3DVertexDeclaration9* s_dcl;
    static bool RegisterDeclarations();
    static void Release(){s_dcl->Release();}
};

/* Usefull wrapper for indexed meshes */
struct Triangle
{
public:
    Triangle()
    {
        m_vertIndex[0] = m_vertIndex[1] = m_vertIndex[2] = 0;
        m_uvIndex[0] = m_uvIndex[1] = m_uvIndex[2] = 0;
        m_normalIndex[0] = m_normalIndex[1] = m_normalIndex[2] = 0;
    }
    int m_vertIndex[3];
    int m_uvIndex[3];
    int m_normalIndex[3];
};

/* Scalable solution for different materials&textures per mesh */
struct OBJ_Material
{
    OBJ_Material() : texture(NULL) {}
    LPDIRECT3DTEXTURE9 texture;
    std::string tex_name;
    std::string name;
};

typedef std::vector<Triangle> vTriangle;
typedef std::vector<D3DXVECTOR3> vVec3;
typedef std::vector<D3DXVECTOR2> vVec2;

/* Allows grouping */
struct OBJ_Group
{
    OBJ_Group() : m_vBuffer(NULL),m_totalVerts(0),m_triangleCount(0) {}
    std::string m_name;
    int m_totalVerts;
    int m_triangleCount;
    vTriangle m_triangles;
    OBJ_Material m_material;
    LPDIRECT3DVERTEXBUFFER9 m_vBuffer;
};

typedef std::vector<OBJ_Group*> vGroups;

/* Manages a vector of OBJ_Groups, also stores all triangle data that is 
   shared between the groups...a waste of memory but allowed for easy
   debugging
*/
class DirectXMesh
{
public:
    DirectXMesh();
    /************************************************************************/
    /* MAIN USE FUCNTIONS                                                   */
    /************************************************************************/
    bool CreateFromOBJ(const char* path, const char* fileName);
    /* Each mesh can have one texture currently, */
    void SetTexture(LPDIRECT3DTEXTURE9 tex);
    /* Procedurally generated shapes */
    bool CreateAsSphere(float rad);
    bool CreateAsRectangle();
    bool CreateAsPlane();
    /* All purpose rendering */
    void Render(ID3DXEffect* effect);
    void CleanUp();
    bool IsShared(){return m_isShared;}
    void MakeSharedMesh(){m_isShared = true;} 
    LPDIRECT3DVERTEXBUFFER9 GenerateSingleBuffer();
/************************************************************************/
/* HELPER FUNCTIONS                                                     */
/************************************************************************/
private:
    /* Create Functions */
    void AssignCurrentMaterial(const std::string& s);
    bool LoadInMaterials( const char* path, const char* fileName);
    void CreateMaterial( const std::string& s, File& file );
    void CreateVertex   (const std::string& s);
    void CreateNormal   (const std::string& s);
    void CreateUv       (const std::string& s);
    void CreateTri      (const std::string& s);
    void CreateGroup    (const std::string& s);
    void GenerateBuffers ();
    void GetFloat       (const std::string& source, std::string* target);
    void Create         (std::string* store, D3DXVECTOR2* v2);
    void Create         (std::string* store, D3DXVECTOR3* v3);
    void GetInts        (int* i, std::string s);
    float StoF          (const std::string& s);
    int StoI            (const std::string& s);

/************************************************************************/
/* GET FUNCTIONS                                                        */
/************************************************************************/
public:
    const std::string& GetFileName(){return m_fileName;}
    const vVec3& Verts() const;
    const D3DXVECTOR3& Vert(int index) const;
    const vVec3& Normals() const;
    const vTriangle& Triangles()const {return m_triangles;}
    void ReverseNormals();
    void ReverseWinding();
    void ReFetchTextures();
    /************************************************************************/
/* MEMBER DATA                                                          */
/************************************************************************/
private:
    //Per mesh data
    vVec2 m_uvs;
    vVec3 m_normals;
    vVec3 m_verts;
    //running total of ALL group stats, for copying over physics data in one go
    //This means we have two copies of triangle data in memory...fix this!
    int m_totalVerts;
    int m_triangleCount;
    int m_numGroups;
    vTriangle m_triangles;
    //we either use an OBJ set of groups
    vGroups m_groups;
    //or this mesh
    LPD3DXMESH m_mesh;
    LPDIRECT3DTEXTURE9 m_meshTexture;
    //current material name, assigned to faces after this is assigned to
    std::string m_currentMaterial;
    std::string m_fileName;
    OBJ_Group* m_currentGroup;
    std::vector<OBJ_Material> m_materials;
    bool m_isShared;
};
#endif
